package edu.northwestern.cbits.purple_robot_manager.probes.services; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.net.Uri; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceManager; import android.preference.PreferenceScreen; import android.widget.Toast; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.TimeZone; import edu.northwestern.cbits.purple_robot_manager.EncryptionManager; import edu.northwestern.cbits.purple_robot_manager.R; import edu.northwestern.cbits.purple_robot_manager.logging.LogManager; import edu.northwestern.cbits.purple_robot_manager.logging.SanityCheck; import edu.northwestern.cbits.purple_robot_manager.logging.SanityManager; import edu.northwestern.cbits.purple_robot_manager.probes.Probe; import edu.northwestern.cbits.xsi.oauth.GitHubApi; import edu.northwestern.cbits.xsi.oauth.Keystore; import edu.northwestern.cbits.xsi.oauth.OAuthActivity; public class GitHubProbe extends Probe { private static final String ENABLED = "config_feature_github_probe_enabled"; private static final boolean DEFAULT_ENABLED = false; private static final String OAUTH_TOKEN = "oauth_github_token"; private static final String OAUTH_SECRET = "oauth_github_secret"; private static final String REPOSITORY_COUNT = "REPOSITORY_COUNT"; private static final String LOGIN = "LOGIN"; private static final String REPOSITORY_NAME = "REPOSITORY_NAME"; private static final String COMMITS_THIS_MONTH = "COMMITS_THIS_MONTH"; private static final String COMMITS_TODAY = "COMMITS_TODAY"; private static final String COMMITS_THIS_WEEK = "COMMITS_THIS_WEEK"; private static final String REPOSITORIES = "REPOSITORIES"; private static final String COMMITS_TOTAL = "COMMITS_TOTAL"; private static final String LAST_REPOSITORY = "LAST_REPOSITORY"; private static final String LAST_COMMIT_MESSAGE = "LAST_COMMIT_MESSAGE"; private long _lastUpdate = 0; private long _lastCommit = 0; @Override public String getPreferenceKey() { return "services_github"; } @Override public String summary(Context context) { return context.getString(R.string.summary_github_probe_desc); } @Override public String probeCategory(Context context) { return context.getResources().getString(R.string.probe_external_services_category); } @Override public String name(Context context) { return "edu.northwestern.cbits.purple_robot_manager.probes.services.GitHubProbe"; } @Override public String title(Context context) { return context.getString(R.string.title_github_probe); } @Override public void enable(Context context) { SharedPreferences prefs = Probe.getPreferences(context); Editor e = prefs.edit(); e.putBoolean(GitHubProbe.ENABLED, true); e.commit(); } @Override public void disable(Context context) { SharedPreferences prefs = Probe.getPreferences(context); Editor e = prefs.edit(); e.putBoolean(GitHubProbe.ENABLED, false); e.commit(); } private void initKeystore(Context context) { Keystore.put(GitHubApi.CONSUMER_KEY, context.getString(R.string.github_client_id)); Keystore.put(GitHubApi.CONSUMER_SECRET, context.getString(R.string.github_client_secret)); } @Override public boolean isEnabled(final Context context) { final SharedPreferences prefs = Probe.getPreferences(context); if (super.isEnabled(context)) { if (prefs.getBoolean(GitHubProbe.ENABLED, false)) { this.initKeystore(context); String token = prefs.getString(GitHubProbe.OAUTH_TOKEN, ""); final String title = context.getString(R.string.title_github_check); final SanityManager sanity = SanityManager.getInstance(context); final GitHubProbe me = this; final long now = System.currentTimeMillis(); if (token == null || token.trim().length() == 0) { String message = context.getString(R.string.message_github_check); Runnable action = new Runnable() { @Override public void run() { me.fetchAuth(context); } }; sanity.addAlert(SanityCheck.WARNING, title, message, action); } else { Keystore.put(GitHubApi.USER_TOKEN, token); sanity.clearAlert(title); if (now - this._lastUpdate > 1000 * 60 * 15) // 15 min refresh interval { this._lastUpdate = now; Runnable r = new Runnable() { public void run() { try { Bundle bundle = new Bundle(); bundle.putString(Probe.BUNDLE_PROBE, me.name(context)); long lastCommit = 0; String lastRepo = null; String lastCommitMessage = null; HashSet<String> repositories = new HashSet<>(); JSONObject user = GitHubApi.fetch(Uri.parse("https://api.github.com/user")); bundle.putString(GitHubProbe.LOGIN, user.getString("login")); JSONArray allEvents = new JSONArray(); for (int i = 1; i <= 10; i++) { JSONArray events = GitHubApi.fetchAll(Uri.parse("https://api.github.com/users/" + user.getString("login") + "/events?page=" + i)); for (int j = 0; j < events.length(); j++) { JSONObject event = events.getJSONObject(j); allEvents.put(event); } } SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); sdf.setTimeZone(TimeZone.getTimeZone("UTC")); long now = System.currentTimeMillis(); HashMap<String, Integer> todayCommits = new HashMap<>(); HashMap<String, Integer> weekCommits = new HashMap<>(); HashMap<String, Integer> monthCommits = new HashMap<>(); HashMap<String, Integer> totalCommits = new HashMap<>(); for (int i = 0; i < allEvents.length(); i++) { JSONObject event = allEvents.getJSONObject(i); String repository = event.getJSONObject("repo").getString("name"); JSONObject payload = event.getJSONObject("payload"); if (payload.has("commits")) { int commitCount = payload.getJSONArray("commits").length(); Date created = sdf.parse(event.getString("created_at")); long timestamp = created.getTime(); if (timestamp > lastCommit) { lastCommit = timestamp; lastRepo = repository; lastCommitMessage = payload.getJSONArray("commits").getJSONObject(0).getString("message"); } Integer today = todayCommits.get(repository); if (today == null) today = 0; Integer week = weekCommits.get(repository); if (week == null) week = 0; Integer month = monthCommits.get(repository); if (month == null) month = 0; Integer total = totalCommits.get(repository); if (total == null) total = 0; if (now - timestamp < (1000 * 60 * 60 * 24)) todayCommits.put(repository, today + commitCount); if (now - timestamp < (1000 * 60 * 60 * 24 * 7)) weekCommits.put(repository, week + commitCount); if (now - timestamp < (1000L * 60L * 60L * 24L * 30L)) monthCommits.put(repository, month + commitCount); totalCommits.put(repository, total + commitCount); repositories.add(repository); } } Bundle repositoriesBundle = new Bundle(); int todayTotal = 0; int weekTotal = 0; int monthTotal = 0; int total = 0; for (String repository : repositories) { Bundle repoBundle = new Bundle(); repoBundle.putString(GitHubProbe.REPOSITORY_NAME, repository); if (todayCommits.containsKey(repository)) { int commits = todayCommits.get(repository); repoBundle.putInt(GitHubProbe.COMMITS_TODAY, commits); todayTotal += commits; } else repoBundle.putInt(GitHubProbe.COMMITS_TODAY, 0); if (weekCommits.containsKey(repository)) { int commits = weekCommits.get(repository); repoBundle.putInt(GitHubProbe.COMMITS_THIS_WEEK, commits); weekTotal += commits; } else repoBundle.putInt(GitHubProbe.COMMITS_THIS_WEEK, 0); if (monthCommits.containsKey(repository)) { int commits = monthCommits.get(repository); repoBundle.putInt(GitHubProbe.COMMITS_THIS_MONTH, commits); monthTotal += commits; } else repoBundle.putInt(GitHubProbe.COMMITS_THIS_MONTH, 0); if (totalCommits.containsKey(repository)) { int commits = totalCommits.get(repository); repoBundle.putInt(GitHubProbe.COMMITS_TOTAL, commits); total += commits; } else repoBundle.putInt(GitHubProbe.COMMITS_TOTAL, 0); repositoriesBundle.putBundle(repository, repoBundle); } bundle.putBundle(GitHubProbe.REPOSITORIES, repositoriesBundle); bundle.putInt(GitHubProbe.REPOSITORY_COUNT, repositories.size()); bundle.putInt(GitHubProbe.COMMITS_TODAY, todayTotal); bundle.putInt(GitHubProbe.COMMITS_THIS_WEEK, weekTotal); bundle.putInt(GitHubProbe.COMMITS_THIS_MONTH, monthTotal); bundle.putInt(GitHubProbe.COMMITS_TOTAL, total); if (lastRepo != null && lastCommitMessage != null) { bundle.putString(GitHubProbe.LAST_REPOSITORY, lastRepo); bundle.putString(GitHubProbe.LAST_COMMIT_MESSAGE, lastCommitMessage); } if (lastCommit != 0) { bundle.putLong(Probe.BUNDLE_TIMESTAMP, lastCommit / 1000); if (lastCommit > me._lastCommit) { me._lastCommit = lastCommit; me.transmitData(context, bundle); } } } catch (Exception e) { LogManager.getInstance(context).logException(e); } } }; Thread t = new Thread(r); t.start(); } } return true; } } return false; } private void fetchAuth(Context context) { String userId = EncryptionManager.getInstance().getUserHash(context); Intent intent = new Intent(context, OAuthActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra(OAuthActivity.CONSUMER_KEY, context.getString(R.string.github_client_id)); intent.putExtra(OAuthActivity.CONSUMER_SECRET, context.getString(R.string.github_client_secret)); intent.putExtra(OAuthActivity.REQUESTER, "github"); intent.putExtra(OAuthActivity.CALLBACK_URL, "http://tech.cbits.northwestern.edu/oauth/github"); intent.putExtra(OAuthActivity.LOG_URL, LogManager.getInstance(context).getLogUrl(context)); intent.putExtra(OAuthActivity.HASH_SECRET, userId); context.startActivity(intent); } @Override public PreferenceScreen preferenceScreen(final Context context, PreferenceManager manager) { final PreferenceScreen screen = super.preferenceScreen(context, manager); screen.setTitle(this.title(context)); screen.setSummary(this.summary(context)); CheckBoxPreference enabled = new CheckBoxPreference(context); enabled.setTitle(R.string.title_enable_probe); enabled.setKey(GitHubProbe.ENABLED); enabled.setDefaultValue(GitHubProbe.DEFAULT_ENABLED); screen.addPreference(enabled); final SharedPreferences prefs = Probe.getPreferences(context); String token = prefs.getString(GitHubProbe.OAUTH_TOKEN, null); String secret = prefs.getString(GitHubProbe.OAUTH_SECRET, null); final Preference authPreference = new Preference(context); authPreference.setTitle(R.string.title_authenticate_github_probe); authPreference.setSummary(R.string.summary_authenticate_github_probe); final Preference logoutPreference = new Preference(context); logoutPreference.setTitle(R.string.title_logout_github_probe); logoutPreference.setSummary(R.string.summary_logout_github_probe); final GitHubProbe me = this; authPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { me.fetchAuth(context); screen.addPreference(logoutPreference); screen.removePreference(authPreference); return true; } }); logoutPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { Editor e = prefs.edit(); e.remove(GitHubProbe.OAUTH_TOKEN); e.remove(GitHubProbe.OAUTH_SECRET); e.commit(); me._lastUpdate = 0; screen.addPreference(authPreference); screen.removePreference(logoutPreference); if (context instanceof Activity) { Activity activity = (Activity) context; activity.runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(context, context.getString(R.string.toast_github_logout), Toast.LENGTH_LONG).show(); } }); } return true; } }); if (token == null || secret == null) screen.addPreference(authPreference); else screen.addPreference(logoutPreference); return screen; } @Override public String summarizeValue(Context context, Bundle bundle) { String lastCommit = bundle.getString(GitHubProbe.LAST_COMMIT_MESSAGE); String lastRepo = bundle.getString(GitHubProbe.LAST_REPOSITORY); String[] tokens = lastRepo.split("/"); if (tokens.length > 1) lastRepo = tokens[1]; return String.format(context.getResources().getString(R.string.summary_github), lastRepo, lastCommit); } }